using System;
using System.Collections.Generic;
using System.Reflection;
using NMock2.Syntax;


namespace NMock2.Events
{
	/// <summary>
	/// helps manage NMock expections.
	/// </summary>
	/// <typeparam name="T">Interface being mocked.</typeparam>
	public class MockExpectationHelper<T> where T : class
	{
		private Dictionary<string, IMatchSyntax> _eventExpectations = new Dictionary<string, IMatchSyntax>();
		private T _objectWithEvents;


		/// <summary>
		/// Constructor
		/// T is interface being mocked.
		/// </summary>
		/// <param name="objectWithEvents">Object implmenting interface.</param>
		public MockExpectationHelper(T objectWithEvents)
		{
			_objectWithEvents = objectWithEvents;
		}


		/// <summary>
		/// Returns the expectation that was
		/// </summary>
		/// <param name="name">Name of event handler to get expectation for.</param>
		/// <returns>NMock expectation.</returns>
		public IMatchSyntax GetEventExpectation(string name)
		{
			if (!_eventExpectations.ContainsKey(name))
			{
				EventInfo eventInfo = GetEvent(typeof (T), name);

				if (eventInfo == null)
					throw new InvalidOperationException(string.Format("{0} is not found on {1}.", name, typeof(T)));
				IMatchSyntax eventExpactation = Expect.Once.On(_objectWithEvents).EventAdd(eventInfo.Name, Is.Anything);
				_eventExpectations.Add(eventInfo.Name, eventExpactation);
			}

			return _eventExpectations[name];
		}

		private  EventInfo GetEvent(Type type, string name)
		{
			EventInfo eventInfo = type.GetEvent(name, BindingFlags.Instance | BindingFlags.Public);
				
			if(eventInfo == null)
			{
				foreach (Type @interface in type.GetInterfaces())
				{
					EventInfo e1 = GetEvent(@interface, name);

					if(e1 != null)
						return e1;
				}

				return null;
			}
			else
			{
				return eventInfo;
			}
		}
	}
}